package com.acmen.easyapi.util;

import com.acmen.easyapi.annotation.Param;
import com.acmen.easyapi.annotation.ReqParam;
import org.springframework.core.annotation.AnnotatedElementUtils;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * 定义一些类通用的方法
 *
 * @Author acmen
 */
public class ClassUtil {

    /**
     *
     * @param clazz 类
     * @param fieldName 字段名
     * @return
     */
    public static Field getField(Class clazz, String fieldName) {
        while (clazz != null && !clazz.equals(Object.class)) {
            try {
                Field field = clazz.getDeclaredField(fieldName);
                return field;
            } catch (NoSuchFieldException e) {
                clazz = clazz.getSuperclass();
            }
        }
        return null;
    }

    /**
     * @param class1
     * @return
     */
    public static List<Field> getFields(Class class1) {
        List<Field> fieldList = new ArrayList<Field>();
        Field[] fields = class1.getDeclaredFields();
        fields:
        for (Field field : fields) {
            for (Field f : fieldList) {
                if (f.getName().equals(field.getName())) {
                    continue fields;
                }
            }
            fieldList.add(field);
        }
        Class<?> superclass = class1.getSuperclass();
        if (superclass != null && superclass != Object.class) {
            List<Field> tempList = getFields(superclass);
            tempList:
            for (Field field : tempList) {
                for (Field f : fieldList) {
                    if (f.getName().equals(field.getName())) {
                        continue tempList;
                    }
                }
                fieldList.add(field);
            }
        }
        return fieldList;
    }


    /**
     *
     * @param field 字段
     * @param annotationClass 字段的所有注解
     * @param <T>
     * @return
     */
    public static <T extends Annotation> T getAnnotation(Field field, Class<T> annotationClass) {
        T target = field.getAnnotation(annotationClass);
        if (target != null) {
            return target;
        }
        return AnnotatedElementUtils.findMergedAnnotation(field, annotationClass);
    }

    /**
     * @param class1
     * @return
     */
    public static List<Field> getFields(Class class1, Class annotationClass) {
        List<Field> fieldList = new ArrayList<Field>();
        Field[] fields = class1.getDeclaredFields();
        fields:
        for (Field field : fields) {
            Annotation annotation = getAnnotation(field, annotationClass);
            if (annotation != null) {
                for (Field f : fieldList) {
                    if (f.getName().equals(field.getName())) {
                        continue fields;
                    }
                }
                fieldList.add(field);
            }
        }
        Class<?> superclass = class1.getSuperclass();
        if (superclass != null && superclass != Object.class) {
            List<Field> tempList = getFields(superclass, annotationClass);
            tempList:
            for (Field field : tempList) {
                for (Field f : fieldList) {
                    if (f.getName().equals(field.getName())) {
                        continue tempList;
                    }
                }
                fieldList.add(field);
            }
        }
        return fieldList;
    }


    /**
     * 取一个实体的指定属性值
     *
     * @param object
     * @param fieldName
     * @return
     */
    public static Object getFieldValueByName(Object object, String fieldName) {
        if (object == null) {
            return null;
        }
        try {
            Field field = object.getClass().getDeclaredField(fieldName);
            return getFieldValue(object, field);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }


    /**
     *获取字段值
     * @param object 对象
     * @param field 字段
     * @return
     */
    public static Object getFieldValue(Object object, Field field) {
        if (object == null) {
            return null;
        }
        String getter = getGetter(field);
        try {
            Method method = object.getClass().getMethod(getter);
            Object value = method.invoke(object);
            return value;
        } catch (Exception e) {
            Boolean accessible = field.isAccessible();
            if (!accessible) {
                field.setAccessible(true);
            }
            try {
                return field.get(object);
            } catch (IllegalAccessException ex) {
                ex.printStackTrace();
                return null;
            } finally {
                if (!accessible) {
                    field.setAccessible(accessible);
                }
            }
        }
    }

    public static String getGetter(Field field) {
        String fieldName = field.getName();
        String firstLetter = fieldName.substring(0, 1).toUpperCase();
        String getter = null;
        if (field.getType().getName().equals("boolean")) {
            if (fieldName.startsWith("is")) {
                getter = fieldName;
            } else {
                getter = "is" + firstLetter + fieldName.substring(1);
            }
        } else {
            getter = "get" + firstLetter + fieldName.substring(1);
        }
        return getter;
    }

    /**
     *获取字段值
     * @param object 对象
     * @param field 字段
     * @return
     */
    public static Object setFieldValue(Object object, Field field,Object value) {
        if (object == null) {
            return null;
        }
        String fieldName = field.getName();
        String firstLetter = fieldName.substring(0, 1).toUpperCase();
        String setter = "set" + firstLetter + fieldName.substring(1);;
        try {
            Method method = object.getClass().getMethod(setter,field.getType());
            return method.invoke(object,value);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 把b的成员变量的值给a
     * @param a
     * @param b
     */
    public static void assign(Object a, Object b) {
        for (Field f : getFields(a.getClass())) {
            for(Field f2 : getFields(b.getClass())){
                if(f2.getName().equals(f.getName())){
                    Object value = getFieldValue(a,f);
                    setFieldValue(b,f2,value);
                    break;
                }
            }
        }
    }

    /**
     * 判断是否DTO对象
     *
     * @param type
     * @return
     */
    public static boolean isDtoType(Type type) {
        if(type instanceof Class){
            Class clazz = (Class)type;
            if(clazz.isArray()){
                return isDtoType(clazz.getComponentType());
            }
            if(clazz.getDeclaredAnnotation(ReqParam.class)!=null){
                return true;
            }
            if(clazz.getDeclaredAnnotation(Param.class)!=null){
                return true;
            }
            Field[] fields = clazz.getDeclaredFields();
            fields:
            for (Field field : fields) {
                Param param = ClassUtil.getAnnotation(field, Param.class);
                if (param != null) {
                    return true;
                }
            }
            Class<?> superclass = clazz.getSuperclass();
            if (superclass != null && superclass != Object.class) {
                return isDtoType(superclass);
            }
            return false;
        }else if (type instanceof ParameterizedType) {
            //ParameterizedType表示一种参数化的类型 List list = new ArrayList<String>();
            ParameterizedType parameterizedType = (ParameterizedType) type;
            //例如List<String> ids,rawType为List
            Type rawType = parameterizedType.getRawType();
            boolean res =  isDtoType(rawType);
            if(res){
                return true;
            }
            Type[] types = parameterizedType.getActualTypeArguments();
            for(Type t : types){
                boolean r =  isDtoType(t);
                if(r){
                    return r;
                }
            }
            return false;
        }else{
            return false;
        }
    }


    /**
     * 获取公开的成员字段
     *
     * @param clazz
     * @return
     */
    public static List<Method> getDeclaredMethods(Class clazz) {
        List<Method> methodList = new ArrayList<Method>();
        Method[] methods = clazz.getDeclaredMethods();
        for (Method method : methods) {
            if (!methodList.contains(method)) {
                methodList.add(method);
            }
        }
        if (!Object.class.equals(clazz.getSuperclass())) {
            methodList.addAll(getDeclaredMethods(clazz.getSuperclass()));
        }
        return methodList;
    }

    public static Method getDeclaredMethod(Class clazz,String methodName, Class<?>... parameterTypes) {
        try {
            return clazz.getDeclaredMethod(methodName,parameterTypes);
        } catch (NoSuchMethodException e) {
            if (!Object.class.equals(clazz.getSuperclass())) {
                return getDeclaredMethod(clazz.getSuperclass(),methodName,parameterTypes);
            }else{
                return null;
            }
        }
    }

    /**
     * 是否为基础类型
     * @param dataType
     * @return
     */
    public static boolean isBasicType(Type dataType) {
        return dataType.equals(String.class)
                || dataType.equals(BigDecimal.class)
                || dataType.equals(Integer.class)
                || dataType.equals(Float.class)
                || dataType.equals(Double.class)
                || dataType.equals(Short.class)
                || dataType.equals(Long.class)
                || dataType.equals(Boolean.class)
                || dataType.equals(LocalTime.class)
                || dataType.equals(LocalDate.class)
                || dataType.equals(LocalDateTime.class)
                || dataType.getTypeName().equals("int")
                || dataType.getTypeName().equals("float")
                || dataType.getTypeName().equals("double")
                || dataType.getTypeName().equals("short")
                || dataType.getTypeName().equals("long")
                || dataType.getTypeName().equals("boolean")
                || (dataType instanceof Class && Map.class.isAssignableFrom((Class) dataType));
    }
    public static boolean isNumericType(Type dataType) {
        return dataType.equals(BigDecimal.class)
                || dataType.equals(Integer.class)
                || dataType.equals(Float.class)
                || dataType.equals(Double.class)
                || dataType.equals(Short.class)
                || dataType.equals(Long.class)
                || dataType.getTypeName().equals("int")
                || dataType.getTypeName().equals("float")
                || dataType.getTypeName().equals("double")
                || dataType.getTypeName().equals("short")
                || dataType.getTypeName().equals("long");
    }


}
